package com.winsdom.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.winsdom.dto.PaymentDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.winsdom.dto.PaymentReceiptDTO;
import com.winsdom.dto.PaymentReceiptImportDTO;
import com.winsdom.dto.RefundOrderDTO;
import com.winsdom.entity.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.winsdom.dto.*;
import com.winsdom.dto.resource.CancelClaimtQueryDTO;
import com.winsdom.exception.BusinessException;
import com.winsdom.exception.ExcelException;
import com.winsdom.mapper.*;
import com.winsdom.mapper.BillsMapper;
import com.winsdom.mapper.PaymentReceiptMapper;
import com.winsdom.result.PageResult;
import com.winsdom.service.CustomerService;
import com.winsdom.service.ICustomerService;
import com.winsdom.service.IPaymentReceiptService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.winsdom.service.ISysUserService;
import com.winsdom.utils.AliOssUtil;
import com.winsdom.vo.PaymentReceiptVO;
import com.winsdom.vo.BillDetailsVO;
import com.winsdom.vo.PaymentVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.winsdom.vo.ReceivePaymentVO;
import com.winsdom.vo.resource.ClaimRecordVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 收款记录表 服务实现类
 * </p>
 *
 * @author 
 * @since 2025-05-22
 */
@Slf4j
@Service
public class PaymentReceiptServiceImpl extends ServiceImpl<PaymentReceiptMapper, PaymentReceipt> implements IPaymentReceiptService {

    @Autowired
    private PaymentReceiptMapper paymentReceiptMapper;
    @Autowired
    private RefundOrderMapper refundOrderMapper;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private CustomerBillingHeaderMapper customerBillingHeaderMapper;
    @Autowired
    private BillsMapper billsMapper;
    @Autowired
    private BillingMainMapper billingMainMapper;

    @Override
    public PageResult getCollectionRecord(PaymentDTO paymentDTO) {
        Page<PaymentVO> page = new Page<>(paymentDTO.getCurrentPage(),paymentDTO.getPageSize());
        List<PaymentVO> list  = paymentReceiptMapper.findCollectionRecord(page,paymentDTO);
        PageResult pageResult = new PageResult();
        pageResult.setPageNum(paymentDTO.getCurrentPage());
        pageResult.setPageSize(paymentDTO.getPageSize());
        pageResult.setTotal(page.getTotal());
        pageResult.setRecords(list);
        return pageResult;

    }

    @Override
    public Page<ClaimRecordVO> seleCancelClaimt(Integer pageNum, Integer pageSize, CancelClaimtQueryDTO billsId) {
        return paymentReceiptMapper.seleCancelClaimt(new Page<>(pageNum,pageSize),billsId);
    }

    @Override
    public void CancelClaim(List<Integer> paymentId) {
        for (Integer id:paymentId){
            paymentReceiptMapper.CancelClaim(id);
        }
    }

    @Override
    public void Claim(ClaimDTO claimDTO) {
        Bills bills = billsMapper.selectById(claimDTO.getBillsId());
        if(bills == null){
            throw new BusinessException("账单不存在", HttpStatus.BAD_REQUEST);
        }
        List<PaymentReceipt> paymentReceipts = paymentReceiptMapper.selectList(
                new LambdaQueryWrapper<PaymentReceipt>()
                        .in(PaymentReceipt::getPaymentId, claimDTO.getPaymentId())
        );

// 检查是否所有ID都有对应的记录
        Set<Long> foundIds = paymentReceipts.stream()
                .map(PaymentReceipt::getPaymentId)
                .collect(Collectors.toSet());

        Set<Long> missingIds = new HashSet<>(claimDTO.getPaymentId());
        missingIds.removeAll(foundIds);

        if (!missingIds.isEmpty()) {
            throw new IllegalArgumentException("收款ID不存在: " + missingIds);
        }
        paymentReceiptMapper.claim(claimDTO);
    }

    @Override
    public PageResult getReceivePaymentByBillsId(ReceivePaymentDTO receivePaymentDTO) {
        Page<ReceivePaymentVO> page = new Page<>(receivePaymentDTO.getCurrentPage(),receivePaymentDTO.getPageSize());
        List<ReceivePaymentVO> list = paymentReceiptMapper.selectReceivePaymentByBillsId(page,receivePaymentDTO);
        PageResult pageResult = new PageResult();
        pageResult.setPageNum(receivePaymentDTO.getCurrentPage());
        pageResult.setPageSize(receivePaymentDTO.getPageSize());
        pageResult.setTotal(page.getTotal());
        pageResult.setRecords(list);
        return pageResult;
    }



    @Override
    public PageResult getPaymentReceiptList(PaymentReceiptDTO paymentReceiptDTO) {

        //定义查询包装器
        LambdaQueryWrapper<PaymentReceipt> queryWrapper = new LambdaQueryWrapper<>();

        //判断收款日期的合法性
        LocalDateTime receiptStartTime = paymentReceiptDTO.getReceiptStartTime();
        LocalDateTime receiptEndTime = paymentReceiptDTO.getReceiptEndTime();
        if ((!Objects.isNull(receiptStartTime)) && (!Objects.isNull(receiptEndTime))) {

            //判断起始日期是否超过结束日期
            if (receiptStartTime.isAfter(receiptEndTime)) {
                log.error("起始日期为{}，结束日期为{}", receiptStartTime, receiptEndTime);
                throw new BusinessException("查询起始日期不能超过结束日期", HttpStatus.BAD_REQUEST);
            }

            queryWrapper.ge(PaymentReceipt::getCreateTime, receiptStartTime);
            queryWrapper.le(PaymentReceipt::getCreateTime, receiptEndTime);
        }

        //判断收款流水号是否存在
        String receiptSerial = paymentReceiptDTO.getReceiptSerial();
        if (StringUtils.hasText(receiptSerial)) {
            queryWrapper.like(PaymentReceipt::getPaymentSerial, receiptSerial);
        }

        //判断支付流水号是否存在
        String paymentSerial = paymentReceiptDTO.getPaymentSerial();
        if (StringUtils.hasText(paymentSerial)) {
            queryWrapper.like(PaymentReceipt::getPaymentSerial, paymentSerial);
        }

        //判断付款人是否存在
        String payerName = paymentReceiptDTO.getPayerName();
        if (StringUtils.hasText(payerName)) {
            queryWrapper.like(PaymentReceipt::getPayerName, payerName);
        }

        //判断付款业主是否存在
        String payerCustomerName = paymentReceiptDTO.getPayerCustomerName();
        if (StringUtils.hasText(payerCustomerName)) {
            queryWrapper.like(PaymentReceipt::getPayerCustomerName, payerCustomerName);
        }

        //判断付款方式是否存在
        Integer paymentMethod = paymentReceiptDTO.getPaymentMethod();
        if (paymentMethod != null) {
            queryWrapper.eq(PaymentReceipt::getPaymentMethod, paymentMethod);
        }

        //判断收款人是否存在
        String payeeName = paymentReceiptDTO.getPayeeName();
        if (StringUtils.hasText(payeeName)) {
            queryWrapper.like(PaymentReceipt::getPayeeName, payeeName);
        }

        //判断收款状态是否存在
        Integer status = paymentReceiptDTO.getStatus();
        if (status != null) {
            queryWrapper.eq(PaymentReceipt::getStatus, status);
        }

        queryWrapper.orderByDesc(PaymentReceipt::getReceiptTime);

        //分页条件查询所需记录
        Integer pageNum = paymentReceiptDTO.getPageNum();
        Integer pageSize = paymentReceiptDTO.getPageSize();
        Page<PaymentReceipt> paymentReceiptPage = paymentReceiptMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
        List<PaymentReceipt> records = paymentReceiptPage.getRecords();

        List<PaymentReceiptVO> paymentReceiptVOList = new ArrayList<>();
        records.forEach(record -> {
            PaymentReceiptVO paymentReceiptVO = new PaymentReceiptVO();
            BeanUtils.copyProperties(record, paymentReceiptVO);
            paymentReceiptVO.setSurplusAmount(record.getReceiptAmount().subtract(record.getSettledAmount()).subtract(record.getRefundAmount()));
            paymentReceiptVOList.add(paymentReceiptVO);
        });

        //封装返回结果到PageResult
        PageResult pageResult = new PageResult();
        pageResult.setTotal(paymentReceiptPage.getTotal());
        pageResult.setPageNum(pageNum);
        pageResult.setPageSize(pageSize);
        pageResult.setRecords(paymentReceiptPage.getRecords());

        return pageResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundWithSuccessStatus(RefundOrderDTO refundOrderDTO) {

        //判断当前关联的收款记录是否是成功支付状态
        Long orderId = refundOrderDTO.getOrderId();
        PaymentReceipt paymentReceiptById = this.lambdaQuery().eq(PaymentReceipt::getPaymentId, orderId).one();

        if (Objects.isNull(paymentReceiptById)) {
            throw new BusinessException("未找到关联收款记录信息", HttpStatus.BAD_REQUEST);
        }

        Integer status = paymentReceiptById.getStatus();

        if (Objects.isNull(status) || status != 1){
            throw new BusinessException("收款记录状态异常", HttpStatus.BAD_REQUEST);
        }

        //验证退款金额是否正确
        BigDecimal receiptAmount = paymentReceiptById.getReceiptAmount();//收款金额
        BigDecimal settledAmount = paymentReceiptById.getSettledAmount();//已核销金额

        //如果没有已核销金额，就设为0
        if (Objects.isNull(settledAmount)){
            settledAmount = BigDecimal.ZERO;
        }

        BigDecimal surplusAmount = receiptAmount.subtract(settledAmount);//计算出的应退款金额

        if (surplusAmount.compareTo(paymentReceiptById.getRefundAmount()) == 0){
            throw new BusinessException("退款金额已结清，无需再退款", HttpStatus.BAD_REQUEST);
        }

        //判断实际退款金额与实际前端返回的退款金额是否正确
        if (surplusAmount.compareTo(refundOrderDTO.getRefundAmount()) != 0){
            log.error("后端计算退款金额：{}，前台传回退款金额：{}",surplusAmount,refundOrderDTO.getRefundAmount());
            throw new BusinessException("退款金额后台计算错误，请联系管理员", HttpStatus.BAD_REQUEST);
        }


        //以上验证都通过，进入下一步插入退款信息
        RefundOrder refundOrder = new RefundOrder();

        BeanUtils.copyProperties(refundOrderDTO, refundOrder);
        String refundNo = UUID.randomUUID().toString();
        refundOrder.setRefundNo(refundNo.substring(refundNo.length()-32));//退款订单号
        refundOrder.setOrderId(orderId);//关联的收款记录id
        refundOrder.setStatus(0);//进入待处理状态
        refundOrder.setCreateTime(LocalDateTime.now());//创建时间
        refundOrder.setUpdateTime(LocalDateTime.now());//修改时间

        //是否上传授权图片
        String authImages = refundOrderDTO.getAuthImages();
        if (StringUtils.hasText(authImages)){
            refundOrder.setAuthImages(authImages);
        }

        //所有退款信息收集后，将数据存到数据库中并修改退款金额属性
        refundOrderMapper.insert(refundOrder);

        BigDecimal refundAmount = paymentReceiptById.getRefundAmount();
        if (Objects.isNull(refundAmount)){
            refundAmount = BigDecimal.ZERO;
        }

        this.lambdaUpdate()
                .set(PaymentReceipt::getRefundAmount, refundAmount.add(refundOrder.getRefundAmount()))
                .eq(PaymentReceipt::getPaymentId, orderId)
                .update();
    }

    @Override
    public void deletePaymentReceiptById(Long id) {

        //判断id是否查到数据
        PaymentReceipt paymentReceipt = this.lambdaQuery().eq(PaymentReceipt::getPaymentId, id).one();
        if (Objects.isNull(paymentReceipt)){
            throw new BusinessException("未找到该记录", HttpStatus.BAD_REQUEST);
        }

        //判断该记录的状态是否未可删除状态，即状态为失败
        Integer status = paymentReceipt.getStatus();
        if (status!=null && status == 2){
            this.removeById(id);
        }else{
            log.error("收款记录id：{}，状态为{}",id,paymentReceipt.getStatus());
            throw new BusinessException("收款记录状态异常", HttpStatus.BAD_REQUEST);
        }

    }

    @Override
    public void importExcelToData(MultipartFile file) {
        //判断是否导入文件
        if (Objects.isNull(file)){
            throw new ExcelException("请选择要导入的excel文件", HttpStatus.BAD_REQUEST);
        }

        String originalFilename = file.getOriginalFilename();
        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));

        //判断是否是excel文件
        if (!(extension.equals(".xlsx") || extension.equals(".xls"))){
            log.error("文件后缀为：{}",extension);
            throw new ExcelException("请上传正确的excel文件", HttpStatus.BAD_REQUEST, file);
        }

        //将导入的文件写入集合中
        List<PaymentReceiptImportDTO> paymentReceiptImportDTOList = null;

        try {
            paymentReceiptImportDTOList = EasyExcel.read(file.getInputStream())
                    .head(PaymentReceiptImportDTO.class)
                    .sheet()
                    .doReadSync();
            // 创建验证器
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

            // 验证每个导入的数据
            for (PaymentReceiptImportDTO dto : paymentReceiptImportDTOList) {
                Set<ConstraintViolation<PaymentReceiptImportDTO>> violations = validator.validate(dto);
                if (!violations.isEmpty()) {
                    StringBuilder errorOSMessage = new StringBuilder("导入数据校验失败：");//系统错误信息
                    StringBuilder errorWindowsMessage = new StringBuilder("请检查字段：");//用户看到的错误信息

                    for (ConstraintViolation<PaymentReceiptImportDTO> violation : violations) {
                        errorOSMessage.append(violation.getPropertyPath()).append(" ").append(violation.getMessage()).append(";");
                        errorWindowsMessage.append(violation.getMessage()).append(";");
                    }
                    log.error("校验失败的数据{}",errorOSMessage.toString());
                    throw new ExcelException(errorWindowsMessage.toString(), HttpStatus.BAD_REQUEST, file);
                }
            }
        } catch (IOException e) {
            throw new ExcelException("导入失败", HttpStatus.BAD_REQUEST, file);
        }


        //预先从数据库中获取所有的客户姓名
        Set<String> userName = sysUserMapper.selectList(null).stream()
                .map(SysUser::getName)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(HashSet::new));

        //过滤掉存在的用户姓名
        Set<String> payeeNameSet = paymentReceiptImportDTOList.stream()
                .map(PaymentReceiptImportDTO::getPayeeName)
                .filter(Objects::nonNull)
                .filter(name -> !userName.contains(name))
                .collect(Collectors.toSet());

        //如果都过滤完，说明用户都存在于数据库中
        if (!payeeNameSet.isEmpty()){
            throw new ExcelException("收款人不为空时，必须是系统中已存在账名的名称", HttpStatus.BAD_REQUEST, file);
        }

        //定义可选择的收款方式
        Map<String,Integer> paymentMethodMap = new HashMap<>(4);
        paymentMethodMap.put("现金",1);
        paymentMethodMap.put("支票",2);
        paymentMethodMap.put("微信支付",3);

        //将收款方式字符改为对应的数值
        for (PaymentReceiptImportDTO paymentReceiptImportDTO : paymentReceiptImportDTOList) {

            String paymentMethodStr = paymentReceiptImportDTO.getPaymentMethodStr().trim();
            if (paymentMethodMap.containsKey(paymentMethodStr)){
                paymentReceiptImportDTO.setPaymentMethod(paymentMethodMap.get(paymentMethodStr));
            }else{
                throw new ExcelException("付款方式仅支持现金、支票和微信支付", HttpStatus.BAD_REQUEST, file);
            }

        }
        //将导入的数据封装到VO集合里面
        List<PaymentReceipt> paymentReceiptList = new ArrayList<>();
        for (PaymentReceiptImportDTO paymentReceiptImportDTO : paymentReceiptImportDTOList) {
            PaymentReceipt paymentReceipt = new PaymentReceipt();
            BeanUtils.copyProperties(paymentReceiptImportDTO, paymentReceipt);
            //获取customerId
            QueryWrapper<Customer> getCustomerQueryWrapper = new QueryWrapper<>();
            getCustomerQueryWrapper.eq("customer_name",paymentReceiptImportDTO.getPayerName());
            Customer customer = customerMapper.selectOne(getCustomerQueryWrapper);
            paymentReceipt.setCustomerId(customer.getCustomerId());
            paymentReceiptList.add(paymentReceipt);
        }

        //匹配付款人，生成付款业主
        //获取付款人姓名
        Set<String> payerNames = paymentReceiptList.stream()
                .map(PaymentReceipt::getPayerName)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        //查询付款人对应的付款业主
        Map<String,String> resultMap = new HashMap<>(2048);
        for (String payerName : payerNames) {
            String customerName = customerBillingHeaderMapper.selectBuyerNameByCustomerName(payerName);
            resultMap.put(payerName,customerName);
        }
        //将查询到的业主信息保存在VO里面
        for (PaymentReceipt paymentReceipt : paymentReceiptList) {
            String payerName = paymentReceipt.getPayerName();
            if (resultMap.containsKey(payerName)){
                paymentReceipt.setPayerCustomerName(resultMap.get(payerName));
            }
        }

//        super.saveBatch(paymentReceiptList);

        //导入成功后的操作
        ImportSuccessAfterDo(paymentReceiptList);
    }

    private void ImportSuccessAfterDo(List<PaymentReceipt> paymentReceiptList) {

        for (PaymentReceipt paymentReceipt : paymentReceiptList) {
            //1，通过付款业主拿到付款人id，并与付款人比对，最后剩下的就是唯一的付款人id
//            String payerCustomerName = paymentReceipt.getPayerCustomerName();
//            String payerName = paymentReceipt.getPayerName();
//            Long customerId = customerBillingHeaderMapper
//                    .getCustomerIdByBuyerNameAndCustomerName(payerCustomerName,payerName);
            //获取查询条件
            Long customerId = paymentReceipt.getCustomerId();
            BigDecimal receiptAmount = paymentReceipt.getReceiptAmount();
            //定义计费主表查询包装器，查询满足条件的计费主表记录
            LambdaQueryWrapper<BillingMain> billingMainLambdaQueryWrapper =
                    new LambdaQueryWrapper<BillingMain>()
                            .eq(BillingMain::getCustomerId, customerId);
            List<BillingMain> billingMainList = billingMainMapper.selectList(billingMainLambdaQueryWrapper);

            //过滤出满足条件的计费主表id
            List<Long> billingMainIdList = billingMainList.stream()
                    .map(BillingMain::getId)
                    .collect(Collectors.toList());

            //查询出账单表中状态为未结清，并且欠款金额和收款金额相等的列表按照升序排序
            LambdaQueryWrapper<Bills> billsLambdaQueryWrapper = new LambdaQueryWrapper<Bills>()
                    .in(Bills::getBillingMainId, billingMainIdList)
                    .eq(Bills::getArrearsStatus, 1)//账单未结清
                    .eq(Bills::getArrearsAmount,receiptAmount)//收款金额与欠款金额相等
                    .orderByAsc(Bills::getCreateTime);
            List<Bills> billsList = billsMapper.selectList(billsLambdaQueryWrapper);

            if (!billsList.isEmpty()){
            /*
            如果付款业主未结清账单中存在欠款金额与收款金额相同时，则该账单与当前收款记录自动进行核销；
            实际上当我查到的列表只有一条，就说明这是当前的账单，若为多个，默认就是第一个为最早的
            若存在多条账单金额与收款金额相同时，则自动核销时间最小的账单（即最早的账单）
             */
                Bills bills = billsList.get(0);
                bills.setArrearsStatus(2);
                billsMapper.updateById(bills);
                //将收款记录状态改为成功1
                paymentReceipt.setStatus(1);

            }
        }

        //将数据保存在数据库中
        super.saveBatch(paymentReceiptList);

    }

}
